package com.zhdl.network.modbus.analysis;

import cn.hutool.core.util.HexUtil;
import com.project.command.faq.analysis.ComAnalysis;
import com.project.command.faq.analysis.ComAnalysisData;
import com.project.command.faq.analysis.ComAnalysisResult;
import com.project.common.util.CRC16;
import com.zhdl.network.modbus.dto.ModbusDto;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service(ModbusAnalysis.MODBUS_ANALYSIS)
public class ModbusAnalysis implements ComAnalysis<ModbusDto, ByteBuf> {

    public static final String MODBUS_ANALYSIS = "modbus_rtu";

    ByteBuf buf = Unpooled.buffer();// 无限制长度


    /**
     * com原始返回数据,进行解析
     * 将ByteBuf 数据,转换为list初步解析对象
     * 接收示例数据 01 05 00 00 FF 00 8C 3A
     *
     * @param data
     * @return 可能多个数据包
     */
    @Override
    public ComAnalysisResult<ModbusDto> analysisData(ComAnalysisData<ByteBuf> data) {
        List<ModbusDto> fullFrames = new ArrayList<ModbusDto>();
        try {
            // 循环读取数据流中的数据，一个字节一个字节的读取
            // mark当前Index，如果无法处理，则重置index，再有数据时仍然从头开始匹配
            buf = data.getBuf();
            buf.markReaderIndex();
            int breakCnt = 0;
            while (buf.isReadable()) {
                buf.readBytes(breakCnt);   // 跳过上次循环读的n个字节
                breakCnt++;
                // 判断是否满足字节最小数目,不满足直接return等待数据到来
//                if (buf.readableBytes() < 7) {
//                    log.info("读取数据长度小于最小长度");
//                    buf.resetReaderIndex();
//                    return new ComAnalysisResult<>(fullFrames);
//                }
                ModbusDto modbusDto = new ModbusDto();
                // 记录读取Index
                int readIndex = buf.readerIndex();

                // 读一个字节判断，一次只读一个字节，无法处理时读下一个字节，能正常解析时，读完整数据包的长度
                modbusDto.setAddress(buf.readByte()); // 地址
                byte cmdType = buf.readByte(); // 功能码
                modbusDto.setCmdType(cmdType);
                //if (cmdType == (byte) 0x05) {  //功能码

                if (cmdType == (byte) 0x05 || cmdType == (byte) 0x06 || cmdType == (byte) 0x10) {  //功能码 0x06,0x10：写数据寄存器返回 6-26
                    // (0x05) 写单个线圈寄存器 00 00 FF 00 8C 3A
                    // 00 00 写单个线圈寄存器地址 2HEX 高字节 低字节
                    // FF 00 写入数据，2HEX 继电器吸合：FF00 继电器断开：0000
                    byte[] addBuff = new byte[2];
                    buf.readBytes(addBuff);

                    byte[] unitBuff = new byte[2];
                    buf.readBytes(unitBuff);
                    modbusDto.setUnitBuff(unitBuff);
                } else if (cmdType == (byte) 0x03 || cmdType == (byte) 0x01) {
                    byte[] length = new byte[1];
                    buf.readBytes(length);
                    byte[] unitBuff = new byte[length[0]];
                    buf.readBytes(unitBuff);
                    modbusDto.setUnitBuff(unitBuff);
                } else {
                    byte[] unitBuff = new byte[1];
                    buf.readBytes(unitBuff);
                    modbusDto.setUnitBuff(unitBuff);
//                    ByteBuffer fullFrame = ByteBuffer.allocate(buf.readerIndex() - readIndex);
//                    buf.getBytes(readIndex, fullFrame);
//                    modbusDto.setFullBuff(fullFrame.array());
//                    return new ComAnalysisResult<>(fullFrames);
                }

                byte[] crcBs = new byte[2];
                buf.readBytes(crcBs);

                ByteBuffer fullFrame = ByteBuffer.allocate(buf.readerIndex() - readIndex);
                buf.getBytes(readIndex, fullFrame);
                modbusDto.setFullBuff(fullFrame.array());


                // crc校验
                byte[] calcBytes = new byte[modbusDto.getFullBuff().length - 2];
                System.arraycopy(modbusDto.getFullBuff(), 0, calcBytes, 0, calcBytes.length);
                byte[] crc = CRC16.MODBUS(calcBytes);

                /**
                 * 暂时注释掉签名验证部分信息
                 */
                if (crc[0] != modbusDto.getFullBuff()[modbusDto.getFullBuff().length - 2] || crc[1] != modbusDto.getFullBuff()[modbusDto.getFullBuff().length - 1]) {
                    buf.resetReaderIndex();
                    continue;  // 签名不通过，不是有效数据包
                }


                //  测试签名部分数据是否有效
                log.info(HexUtil.encodeHexStr(calcBytes) + "----------------------0");
                log.info(HexUtil.encodeHexStr(crc) + "----------------------1");


                fullFrames.add(modbusDto);
                breakCnt = 0;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            buf.resetReaderIndex();
            //从头开始读
        }
        log.info("return ComAnalysisResult>>>>>>>>>>>>>>>");
        return new ComAnalysisResult<>(fullFrames);
    }


    /**
     * 获取协议头信息
     *
     * @return
     */
    @Override
    public String getHeadAnalysis() {
        return "";
    }

    /**
     * 协议追加校验位信息（尾部数据）
     *
     * @param cmdStr
     * @return
     */
    @Override
    public byte[] getAfterAnalysis(String cmdStr) {
        return HexUtil.decodeHex(cmdStr);
    }
}
